//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/lexer/treewalk.h"
#include "sagittarius/io/io.h"
#include <stddef.h>
#include <malloc.h>
#include <string.h>

typedef struct {
    size_t len;
    int dyn;
    union {
        LexBuf buf;
        LXAlphaType sin;
    };
} foldresult;

foldresult __foldSingle(LXAlphaType c) {
    //foldresult x = {.len = 1, .buf = &c, .dyn = 0};
    foldresult x;
    x.len = 1;
    LX_FORCE_INIT(x.sin);
    LX_ALPHA_SET(x.sin, c);
    x.dyn = 0;
    return x;
}

void __foldFree(foldresult x) {
    if(x.dyn)
        LX_FREE_BUFFERL(x.buf, x.len);
    else
        LX_ALPHA_CLEAR(x.sin);
}

#if SAGITTARIUS_COMPILER == SAGITTARIUS_COMPILER_GNUC || SAGITTARIUS_COMPILER == SAGITTARIUS_COMPILER_MINGW
inline
#endif
int __foldIsEmpty(foldresult x) {
    if(x.dyn && x.buf == NULL)
        return 1;
    else
        return 0;
}

foldresult __foldJoin(foldresult l, foldresult r) {
	#if SAGITTARIUS_COMPILER == SAGITTARIUS_COMPILER_GNUC || SAGITTARIUS_COMPILER == SAGITTARIUS_COMPILER_MINGW
		foldresult x = {.len = l.len + r.len, .dyn = 1};
	#else
		foldresult x;
		x.len = l.len + r.len;
		x.dyn = 1;
	#endif
    if(__foldIsEmpty(l) || __foldIsEmpty(r)) {
        x.buf = NULL;
        return x;
    }
    //TODO: use a pool?
    x.buf = LX_CREATE_BUFFER(x.len);
    LX_COPY(x.buf,       l.dyn ? l.buf : &l.sin, l.len);
    LX_COPY(x.buf+l.len, r.dyn ? r.buf : &r.sin, r.len);
    __foldFree(l);
    __foldFree(r);
    return x;
}

foldresult __lex_concatFold(lASTNode* n) {
    foldresult x;
	x.buf = NULL;
    if(n->type == AST_NODE_TYPE_REPRIMITIVE) {
        if(!re_CSisSymbol(n->cs))
            return x; //return x with NULL buf: can only fold symbols
        else
            return __foldSingle(re_CSgetSymbol(n->cs));
    }
    if(n->type != AST_NODE_TYPE_RECONCAT)
        return x; //only works on concat nodes
    
    return __foldJoin(__lex_concatFold(n->n1), __lex_concatFold(n->n2));
}

int lex_canFold(lASTNode* n) {
    //SG_WRITE(2, "lex_canFold\n");
    if(n->closed)
        return 0;
    if(n->type == AST_NODE_TYPE_REPRIMITIVE) {
        if(re_CSisSymbol(n->cs))
            return 1;
        else
            return 0;
    } else if(n->type != AST_NODE_TYPE_RECONCAT) {
        return 0;
    } else {
        return lex_canFold(n->n1) && lex_canFold(n->n2);
    }
}

LexBuf lex_concatFold(lASTNode* n) {
    LexBuf buf;
    foldresult x=__lex_concatFold(n);
    AN(!x.dyn || x.buf, "Fold failed");
    buf = LX_CREATE_BUFFER((x.len+1));
    LX_COPY(buf, x.dyn ? x.buf : &x.sin, x.len);
    LX_FORCE_INIT(buf[x.len]);
    LX_ALPHA_SET_SPC(buf[x.len], '\0');
    __foldFree(x);
    return buf;
}

void lxat_doSubstitutions(lASTNode** n, lsymtab* tab) {
    LexBuf buf;
    radixnode* entry; //entry in symtab
    int closed = (*n)->closed;
    int nullable = (*n)->nullable;
    (*n)->closed = 0;
    if(lex_canFold(*n)) {
        /*SG_WRITE(2, "folding node:\n");
        lex_writeOut(2, *n,0);*/
        buf = lex_concatFold(*n);
        /*SG_WRITE(2, "buf:\n");
        LX_WRITE_BUF(2,buf);
        SG_WRITE(2, ";\n");
        dump_radix_node(2, tab->r, 0);*/
        entry = radix_get(tab->r, buf);
        if(entry) {
            //SG_WRITE(2, "Entry\n");
            lAST_destroy_node(*n);
            //*n = (lASTNode*)entry->p;
            //SG_WRITE(2, "deep copying node:\n");
            //lex_writeOut(2, (lASTNode*)entry->p,0);
            *n = lAST_deepCopy((lASTNode*)entry->p);
        } /*else
            SG_WRITE(2, "No entry\n");*/
        LX_FREE_BUFFER(buf);
    } else {
        if((*n)->type != AST_NODE_TYPE_REPRIMITIVE) {
            lxat_doSubstitutions(&(*n)->n1, tab);
            lxat_doSubstitutions(&(*n)->n2, tab);
        }
    }
    (*n)->closed = closed;
    (*n)->nullable = nullable;
}
